#include "config.h"

#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/property_tree/json_parser.hpp>

const char *defaultHost = "127.0.0.1";
const int defaultPort = 9999;
const int defaultWorkers = 1;


template<typename T>
T getValue(const boost::property_tree::ptree &source, const char *key, T Default, std::string &error);

template<typename T>
T getValue(const boost::property_tree::ptree::const_iterator &it_source, const char *key, T Default, std::string &error);


ConfigMaster::ConfigMaster()
    : mHost(defaultHost), mPort(defaultPort)
{
}

ConfigSlot::ConfigSlot()
    : mHost(defaultHost), mPort(defaultPort), mWorkers(defaultWorkers)
{
}


void Config::loadFromFile(const char *fileName, std::string &error)
{
    try
    {
        std::string strFileName = fileName;
        boost::property_tree::read_json(strFileName, mSourceData);
        update(error);
        
    } catch (std::exception const& e)
    {
        error += std::string(e.what()) + "\n";
    }
}

void Config::loadFromString(const char *Data, std::string &error)
{
    try
    {
        std::string str(Data);
        std::stringstream strData;
        strData << str;
        //boost::property_tree::json_parser::read_json();
        boost::property_tree::read_json(strData, mSourceData);
        update(error);
     
    } catch (std::exception const& e)
    {
        error += std::string(e.what()) + "\n";
    }
}

void Config::update(std::string &error)
{
    // Update master
    ConfigMaster defaultsMaster;
    mConfigMaster = defaultsMaster;
    mConfigMaster.mHost = getValue<std::string>(mSourceData, "Master.Host", mConfigMaster.mHost, error);
    mConfigMaster.mPort = getValue<int>(mSourceData, "Master.Port", mConfigMaster.mPort, error);
    
    // Get slots size
    int countSlots = 0;
    std::string strKeySlot("Slot");
    
    for (boost::property_tree::ptree::iterator it = mSourceData.begin(); it != mSourceData.end(); ++it)
        if (it->first == strKeySlot) 
            ++countSlots;

    //Update slots
    mConfigSlots.resize(countSlots);
    mConfigSlots.clear();
    
    for (boost::property_tree::ptree::const_iterator it = mSourceData.begin(); it != mSourceData.end(); ++it)
    {
        if (it->first != strKeySlot) continue;
        
        ConfigSlot slot;
        slot.mHost = getValue<std::string>(it, "Host", mConfigMaster.mHost, error);
        slot.mPort = getValue<int>(it, "Port", slot.mPort, error);
        slot.mWorkers = getValue<int>(it, "Workers", slot.mWorkers, error);
        
        mConfigSlots.push_back(slot);
    }
}

bool Config::getConfigSlot(unsigned int index, ConfigSlot &configSlot)
{
    if (index > (unsigned int)mConfigSlots.size()) return false;
    
    configSlot = mConfigSlots[index];
    return true;
}

void Config::toStream(std::stringstream &output, std::string &error)
{
    try
    {
        write_json( output, mSourceData);
     
    } catch (std::exception const& e)
    {
        error += std::string(e.what()) + "\n";
    }
}


template<typename T>
T getValue(const boost::property_tree::ptree &source, const char *key, T Default, std::string &error)
{
    try 
    {
        return source.get<T>(key);

    } catch(boost::property_tree::ptree_bad_data &err)
    {// Value have invalid type - error
        error += "\"" + std::string(key) + "\": " + err.what() + "\n";
        return Default;
        
    } catch(boost::property_tree::ptree_bad_path &err)
    {// item not found - use default value
        return Default;
        
    }
}

template<typename T>
T getValue(const boost::property_tree::ptree::const_iterator &it_source, const char *key, T Default, std::string &error)
{
    try 
    {
        boost::property_tree::ptree node(it_source->second.get_child(key));
        return boost::lexical_cast<T>(node.data().c_str());

    }catch (std::exception const& e)
    {
        return Default;
            
    } catch(boost::bad_lexical_cast &err)
    {// Value have invalid type - error
        error += "\"" + std::string(key) + "\": " + err.what() + "\n";
        return Default;
        
    } catch(boost::property_tree::ptree_bad_path &err)
    {// item not found - use default value
        return Default;
    }
}
